<template>
  <a-spin class="full-screen-spin" :spinning="isLoading" size="large">
    <div class="task-mark">
      <div class="header flex-between">
        <div class="left-info">
          <div class="logo fc">
            <img src="@/assets/img/header-logo.svg" />
          </div>
          <div class="mark-info">
            <span class="task-name" :title="taskInfo.name" @click="backPage">{{ taskInfo.name }}/</span>
            <span class="mark-title">标注</span>
            <span class="task-type">图像分类</span>
          </div>
        </div>
        <div class="right-progress">
          <a-progress :percent="Number(taskProgressData.progress)" :stroke-color="'#D61860'" />
        </div>
      </div>
      <div class="contain flex-between">
        <!-- 图片列表 -->
        <img-select
          ref="imgSelectRef"
          :task-progress-data="taskProgressData"
          :img-list="imgList"
          :current-mark-img-index="currentMarkImgIndex"
          @clickImg="(index:number) => selectImg(index)"
          @scroll="onImgListScroll"
        />
        <!-- 标注区域 -->
        <div ref="middleMarkRef" class="middle-mark">
          <div class="utils-bar">
            <div class="change-btn">
              <a-button class="btn" :disabled="currentMarkImgIndex == 0" @click="selectImg(currentMarkImgIndex - 1)">
                <i class="iconfont icon-arrow-left-s-line"> </i>
              </a-button>
              <a-button class="btn" :disabled="currentMarkImgIndex == imgList.length - 1" @click="selectImg(currentMarkImgIndex + 1)">
                <i class="iconfont icon-arrow-right-s-line"> </i>
              </a-button>
            </div>
            <div class="mark-tools">
              <span class="tools-btn" :title="'放大'" @click.stop="scaleImg('zoomIn')"><i class="iconfont icon-zoom-in-line"></i></span>
              <span class="tools-btn" :title="'缩小'" @click.stop="scaleImg('zoomOut')"><i class="iconfont icon-zoom-out-line"></i></span>
              <span class="tools-btn" @click.stop="resetImg"><i class="iconfont icon-unzoom-in-line" :title="'重置大小'"></i></span>
              <span :class="[currentType === 'default' && 'tools-btn--active', 'tools-btn', '']" :title="'移动'">
                <i v-if="currentType === 'default'" class="iconfont icon-back_Fist"></i>
                <i v-else class="iconfont icon-back_hand"></i>
              </span>
              <div class="empty"></div>
            </div>
            <div class="save-btn">
              <a-button type="primary" class="btn" @click="onClickSaveMark">
                <span><i class="iconfont icon-check-line"></i></span>
                <span>保存</span>
              </a-button>
            </div>
          </div>
          <div class="mark-do-result">
            <div ref="markAreaRef" class="mark-area">
              <canvas v-if="isRenderCanvas" id="canvas" ref="canvasRef" width="200" height="200" style="border: 1px solid #ccc"></canvas>
            </div>
            <div class="mark-result">
              <div class="result-top">
                <span>标注结果</span>
                <i class="iconfont icon-delete-bin-2-line" @click=";(checkedListItem = []), (checkedList = [])"></i>
              </div>
              <div class="result-list my-scrollbar">
                <div v-for="item in checkedListItem" :key="item.id" class="result-li" :title="item.name">{{ item.name }}</div>
              </div>
            </div>
          </div>
        </div>
        <!-- 标签列表 -->
        <div class="right-tag">
          <div class="tag-tab">
            <div v-if="!tagIsSearch" class="no-search">
              <div class="name">标签</div>
              <div>
                <span class="search-icon" @click="showTagIsSearch(true)"><i class="iconfont icon-search-line"></i></span>
                <span class="add-btn" @click="addTag"><i class="iconfont icon-add-line"></i></span>
              </div>
            </div>
            <div v-else class="search">
              <a-input
                ref="inputRef"
                v-model:value="tagSearchValue"
                :maxlength="20"
                allow-clear
                placeholder="请输入标签"
                @keyup.enter="getTaskTag()"
                @blur="showTagIsSearch(false)"
                @keydown.stop=""
              >
                <template #prefix>
                  <i class="iconfont icon-search-line" @click="getTaskTag()"></i>
                </template>
                <!-- <template #suffix>
                  <div @click=";(tagSearchValue = ''), (tagIsSearch = false), getTaskTag()" class="close-search-btn">
                    <CloseCircleFilled />
                  </div>
                </template> -->
              </a-input>
            </div>
          </div>
          <div class="check-all-box">
            <a-checkbox
              v-show="tagList.length"
              v-model:checked="checkAll"
              :disabled="disableSelectAll"
              :indeterminate="indeterminate"
              @change="onCheckAllChange"
            >
              <div class="check-all-text">名称</div>
            </a-checkbox>
            <!-- 无数据时使用禁用样式 -->
            <a-checkbox v-show="tagList.length === 0" :checked="false" disabled> <div class="check-all-text">名称</div></a-checkbox>
          </div>
          <div class="tag-list my-scrollbar">
            <a-checkbox-group
              v-model:value="checkedList"
              style="display: flex; width: 100%; flex-direction: column"
              @change="onCheckChange"
            >
              <div
                v-for="(item, index) in tagList"
                :key="item.id"
                :class="[(editTagIndex == index || hoverTagIndex == index) && 'tag-li--active', 'checkbox-li']"
              >
                <a-checkbox :value="item.id" :disabled="editTagIndex === index" @change="onItemChange">
                  <div class="tag-li" @mouseover="hoverTagIndex = index" @mouseleave="hoverTagIndex = undefined">
                    <div class="tag-name">
                      <!-- <a-tooltip v-if="editTagIndex !== index" destroyTooltipOnHide>
                        <template #title> {{ item.name }}</template>
                        <span> {{ item.name }} </span>
                      </a-tooltip> -->
                      <span v-if="editTagIndex !== index" :title="item.name"> {{ item.name }} </span>
                      <a-input
                        v-else
                        v-model:value="editTagValue"
                        v-trim
                        :maxlength="20"
                        @keydown.stop=""
                        @keyup.enter="confirmEditTag(item, index)"
                      />
                    </div>
                    <div v-if="hoverTagIndex != index && editTagIndex !== index"></div>
                    <div v-else-if="hoverTagIndex == index && editTagIndex !== index" class="hover-btn" @click.stop>
                      <span class="btn" @click.prevent="editTag(item, index)"><i class="iconfont icon-edit-line"></i></span>
                      <span class="btn" @click.prevent="deleteTag(item)"><i class="iconfont icon-delete-bin-6-line"></i></span>
                    </div>
                    <div v-else class="edit-btn" @click.stop>
                      <span class="btn" @click.prevent="confirmEditTag(item, index)"><i class="iconfont icon-check-line"></i></span>
                      <span class="btn" @click.prevent="cancelEditTag(item)"><i class="iconfont icon-close-line"></i></span>
                    </div>
                  </div>
                </a-checkbox>
              </div>
            </a-checkbox-group>
          </div>
        </div>
      </div>
    </div>
  </a-spin>
</template>

<script lang="ts" setup>
  import { useEventListener } from '@vueuse/core'
  import { message, Modal } from 'ant-design-vue'
  import { fabric } from 'fabric'
  import { cloneDeep } from 'g6-fn'
  import { computed, createVNode, nextTick, onMounted, Ref, ref } from 'vue'
  import { useRoute, useRouter } from 'vue-router'

  import WarnIcon from '@/components/icon/IconWarn/index.vue'
  import useTimeLoading from '@/hooks/use-time-loading'
  import { dmpTaskApi } from '@/services/api'
  import { regex } from '@/utils'

  import ImgSelect from '../components/ImgSelect.vue'
  import { TaskDataLi, TaskTag } from '../types'

  const route = useRoute()
  const router = useRouter()
  const { isLoading, openLoading, closeLoading } = useTimeLoading({ delayTime: 0, timeoutTime: 60000000 })

  const taskId = route.params.taskId as string
  //  路由参数：任务基础信息
  const taskInfo = ref({
    id: '',
    name: ''
  })
  const taskProgressData = ref({
    progress: '0',
    totalCount: 0,
    noMarkCount: 0,
    hasMarkCount: 0
  })
  const currentType = ref('default') // 当前操作类型
  // 标签变量
  const disableSelectAll = ref(false)
  const tagIsSearch = ref(false)
  const editTagIndex = ref<number>(-1)
  const isEditing = computed(() => editTagIndex.value !== -1)
  const editTagValue = ref<string>()
  const hoverTagIndex = ref()

  const checkedList = ref<string[]>([])
  const indeterminate = computed(() => !!(checkedList.value.length && checkedList.value.length < tagList.value.length))
  const checkAll = computed(() => checkedList.value.length === tagList.value.length)
  const checkedListItem = ref<TaskTag[]>([])

  // 标注变量
  const currentMarkImgIndex = ref(-1)
  const currentMarkItem = computed<Partial<TaskDataLi>>(() => {
    if (currentMarkImgIndex.value === -1) {
      return {}
    }
    return imgList.value[currentMarkImgIndex.value]
  })
  const markAreaRef = ref()
  const middleMarkRef = ref()
  const canvasRef = ref()
  const isRenderCanvas = ref(true) //是否显示canvas，用于切换图片时对canvas强行销毁
  let canvas: fabric.Canvas
  let myImage: fabric.Image

  /* 左侧图片渲染数据逻辑----------------------------------------------------------------------------------------------------------------------------------- */
  // 任务图片参数
  type ImgData = TaskDataLi | { id: number }
  const imgList = ref<ImgData[]>([])
  let mockImgList: ImgData[] = [] // 模拟图片列表
  const imgSelectRef = ref()
  let isImgSelectInit = false //是否已经初始化图片选择组件

  //根据总数初始化虚拟图片列表
  const createMockImgList = (total: number) => {
    const list = []
    for (let i = 0; i < total; i++) {
      list[i] = { id: i }
    }
    mockImgList = list
  }
  //判断元素是否为真实任务图片数据
  const judgeIsTaskDataLi = (item: ImgData) => typeof item.id === 'string'
  // 根据图片信息，获取图片当前是第多少张图片
  const getImgStartIndex = async (taskId: string, itemId: string): Promise<number> => {
    const { data: index } = await dmpTaskApi.getImgStartIndex({ id: itemId, taskId })
    return index
  }
  let prePartImgStartIndex: null | number = null //上次获取的部分图片列表的开始索引
  // 获取部分任务真实图片数据
  const getPartImgList = async (_startIndex?: number) => {
    let startIndex = _startIndex || imgSelectRef.value?.startIndex || 0
    const total = taskProgressData.value.totalCount
    // 如果开头数据不足以显示在列表第一个，则更新开始索引到第一个
    if (total - startIndex <= 20) {
      startIndex = total - 20 >= 0 ? total - 20 : 0
    }

    //为0则代表imgList还未加载数据
    if (total === 0) return

    // 防止index一致时重复请求
    if (startIndex === prePartImgStartIndex) return

    prePartImgStartIndex = startIndex

    let params = {
      taskId,
      start: startIndex,
      limit: 20
    }
    let { data } = await dmpTaskApi.partImgList(params)
    const list = data.records

    list.forEach((imgLi: TaskDataLi, i: number) => {
      mockImgList[startIndex + i] = imgLi
    })

    imgList.value = cloneDeep(mockImgList)

    await nextTick()

    isImgSelectInit = true //获取数据则说明图片首次加载完成
  }

  // 根据滚动，动态加载imgList数据
  let imgListScrollTimer: any = null //滚动防抖
  const onImgListScroll = () => {
    if (!isImgSelectInit || isLoading.value) return
    imgListScrollTimer && clearTimeout(imgListScrollTimer)
    imgListScrollTimer = setTimeout(() => {
      console.log('滚动逻辑触发，加载图片')
      getPartImgList()
    }, 200)
  }

  // 获取任务数据
  const getListData = async () => {
    let params = {
      taskId: taskId,
      markType: -1,
      offset: 1,
      limit: 999999999
    }
    let { data } = await dmpTaskApi.taskBucketData(params)
    imgList.value = data.records
  }

  // 选择任务图片
  const selectImg = async (index: number) => {
    openLoading()
    const item = imgList.value[index]

    if (currentMarkImgIndex.value !== -1) {
      await saveMark()
    }

    const isTaskImgLi = judgeIsTaskDataLi(imgList.value[index]) //判断是否为任务图片列表，如果是假数据，则重新获取数据
    if (!isTaskImgLi) {
      await getPartImgList(index)
    }

    currentMarkImgIndex.value = index

    reSetTagValue()

    await rerenderCanvas()
    initCanvas()
    await getMarkInfo(item.id)
    closeLoading()
    console.log('loading关闭')
  }
  /* ----------------------------------------------------------------------------------------------------------------------------------- */

  // 标签参数
  const tagList = ref<TaskTag[]>([])
  const tagSearchValue = ref()

  // TODO: 标注图片
  const loadImage = (url: any) => {
    return new Promise(rs => {
      fabric.Image.fromURL(url, (i: any) => {
        i.hasControls = false // 禁止控制角
        const scale = Math.min(canvas.getWidth() / i.width, canvas.getHeight() / i.height)
        i.scale(scale)
        myImage = i
        // 将图片添加到画布
        canvas.add(myImage)

        // 使图片居中
        canvas.centerObject(myImage)

        // 刷新画布
        canvas.renderAll()

        rs(true)
        console.log(`图片加载完成`)
      })
    })
  }

  // 重新渲染canvas
  const rerenderCanvas = () => {
    // 如果已有canvas节点，直接删除掉
    let canvasElement = document.querySelector('.mark-area .canvas-container')
    canvasElement?.parentNode?.removeChild(canvasElement)

    return new Promise(rs => {
      isRenderCanvas.value = false
      nextTick(async () => {
        isRenderCanvas.value = true
        nextTick(() => rs(true))
      })
    })
  }

  const initCanvas = () => {
    canvas = new fabric.Canvas(canvasRef.value) // 创建画布
    canvas.setWidth(middleMarkRef.value.offsetWidth - 180)
    canvas.setHeight(markAreaRef.value.offsetHeight)

    let isDragging = false
    let draggingObj = { lastPosX: 0, lastPosY: 0 }
    // 在窗口大小变化时重新设置画布尺寸
    canvas.on('mouse:down', (opt: { e: any }) => {
      // 鼠标按下时触发
      let evt = opt.e
      // 是否按住alt
      isDragging = true // isDragging 是自定义的
      draggingObj.lastPosX = evt.clientX // lastPosX 是自定义的
      draggingObj.lastPosY = evt.clientY // lastPosY 是自定义的
    })
    // 鼠标移动时触发
    canvas.on('mouse:move', (opt: { e: any }) => {
      if (isDragging) {
        canvas.setCursor('grabbing')
        let evt = opt.e
        let vpt = canvas.viewportTransform as number[] // 聚焦视图的转换
        vpt[4] += evt.clientX - draggingObj.lastPosX
        vpt[5] += evt.clientY - draggingObj.lastPosY
        canvas.requestRenderAll()
        draggingObj.lastPosX = evt.clientX
        draggingObj.lastPosY = evt.clientY
      } else {
        canvas.setCursor('grab')
      }
    })
    canvas.on('mouse:up', (_opt: any) => {
      console.log('鼠标松开时触发', _opt)

      // 鼠标松开时触发
      canvas.setViewportTransform(canvas.viewportTransform as number[]) // 设置此画布实例的视口转换
      isDragging = false
      canvas.setCursor('grab')
    })

    // 监听鼠标滚动事件
    canvas.on(
      'mouse:wheel',
      (opt: { e: { deltaY: any; offsetX: any; offsetY: any; preventDefault: () => void; stopPropagation: () => void } }) => {
        console.log('鼠标滚动事件', opt, opt.e.offsetX, opt.e.offsetY)

        let delta = opt.e.deltaY // 滚轮，向上滚一下是 -100，向下滚一下是 100
        let zoom = canvas.getZoom() // 获取画布当前缩放值
        zoom *= 0.999 ** delta
        if (zoom > 20) {
          zoom = 20
        }
        if (zoom < 0.01) {
          zoom = 0.01
        }
        canvas.zoomToPoint(
          {
            // 关键点
            x: opt.e.offsetX,
            y: opt.e.offsetY
          },
          zoom
        )

        opt.e.preventDefault()
        opt.e.stopPropagation()
        canvas.requestRenderAll()
      }
    )
  }

  const resetCanvas = () => {
    canvas.setZoom(1)
    canvas.clear()
    canvas.setViewportTransform([1, 0, 0, 1, 0, 0])
  }

  const scaleImg = (type: string) => {
    let delta
    if (type === 'zoomIn') {
      delta = -100 // 滚轮，向上滚一下是 -100，向下滚一下是 100
    } else {
      delta = 100 // 滚轮，向上滚一下是 -100，向下滚一下是 100
    }
    let zoom = canvas.getZoom() // 获取画布当前缩放值
    zoom *= 0.999 ** delta
    if (zoom > 20) {
      zoom = 20
    }
    if (zoom < 0.01) {
      zoom = 0.01
    }
    let canvasCenter: fabric.Point | null = new fabric.Point(canvas.width! / 2, canvas.height! / 2)
    canvas.zoomToPoint(canvasCenter, zoom)
    canvasCenter = null
    canvas.requestRenderAll()
  }
  const resetImg = () => {
    canvas.setZoom(1)
    canvas.setViewportTransform([1, 0, 0, 1, 0, 0])
    myImage.center()
    canvas.discardActiveObject()
  }
  const handleKeyDown = (event: KeyboardEvent) => {
    // 禁用Ctrl + S默认事件 ,因为和浏览器热键有冲突
    if (event.ctrlKey && event.code === 'KeyS') {
      event.preventDefault()
    }

    if (isLoading.value) {
      return
    }

    event.preventDefault()

    if (event.shiftKey && event.code === 'Delete' && !event.altKey && !event.ctrlKey) {
      checkedListItem.value = []
      checkedList.value = []
      return
    }
    if (event.ctrlKey && !event.altKey && !event.shiftKey) {
      // handle the "ctrl+s" event
      if (event.code === 'KeyS') {
        onClickSaveMark()
        return
      }
      return
    }
    if (!event.ctrlKey && !event.altKey && !event.shiftKey) {
      switch (event.code) {
        case 'Escape':
          resetImg()
          return
        case 'KeyA':
          console.log('KeyA')

          if (currentMarkImgIndex.value === 0) {
            return false
          }
          selectImg(currentMarkImgIndex.value - 1)
          return
        case 'KeyD':
          if (currentMarkImgIndex.value === imgList.value.length - 1) {
            return false
          }
          selectImg(currentMarkImgIndex.value + 1)
          return

        default:
          return
      }
    }
  }

  // TODO: 任务
  // 获取任务进度
  const getTaskProgress = async () => {
    let { data } = await dmpTaskApi.taskProgress(taskId)
    if (data) {
      taskProgressData.value = data
    }
  }

  // TODO: 标注
  //重置选中标签值
  const reSetTagValue = () => {
    checkedList.value = []
    checkedListItem.value = []
  }
  // 获取标注信息
  const getMarkInfo = async (id: string | number) => {
    let { data } = await dmpTaskApi.taskDataMarkInfo(id)

    await loadImage(data.fileUrl)

    imgSelectRef.value.selectIndex(currentMarkImgIndex.value)
    let findIndex = imgList.value.findIndex((item: any) => item.id === id)
    if (findIndex !== -1) {
      imgList.value[findIndex].markType = data.markType
    }
    if (!data.markInfoList) {
      reSetTagValue()
      return
    }
    checkedList.value = data.markInfoList.map((item: any) => item.taskTagId)
    checkedListItem.value = tagList.value.filter((item: any) => checkedList.value.includes(item.id))
  }

  const onClickSaveMark = async () => {
    openLoading({ tip: '数据保存中' })
    await saveMark(true)
    closeLoading()
  }

  // 保存标注
  const saveMark = async (isAutoNext = false) => {
    let params = {
      id: currentMarkItem.value.id,
      markInfoList: checkedListItem.value.length > 0 ? checkedListItem.value : null,
      taskType: '1'
    }

    await dmpTaskApi.taskDataMark(params, {
      isAddUid: true
    })

    getTaskProgress()
    // 改变标注状态
    currentMarkItem.value.markType = checkedListItem.value.length > 0 ? 1 : 0
    if (!isAutoNext) {
      return
    }
    if (currentMarkImgIndex.value === imgList.value.length - 1) {
      message.success('保存成功，当前为最后一张图片')
      return
    }
    reSetTagValue()
    currentMarkImgIndex.value += 1
    router.replace({
      query: {
        id: currentMarkItem.value.id,
        taskId: taskId
      }
    })
    console.log(currentMarkItem.value.id)

    canvas.setZoom(1)
    resetCanvas()
    await getMarkInfo(currentMarkItem.value.id)
    canvas.setViewportTransform([1, 0, 0, 1, 0, 0])
    myImage.center()
    canvas.remove(myImage)

    fabric.Image.fromURL(currentMarkItem.value.fileUrl || '', (i: any) => {
      const scale = Math.min(canvas.getWidth() / i.width, canvas.getHeight() / i.height)
      i.scale(scale)
      myImage = i
      myImage.hasControls = false // 禁止控制角
      // 将图片添加到画布
      canvas.add(myImage)
      // 使图片居中
      canvas.centerObject(myImage)
      myImage.center()

      // 刷新画布
      canvas.renderAll()
    })
  }

  /* 标签逻辑-------------------------------------------------------------------------------------------------------------------------*/
  // 获取标签列表
  const getTaskTag = async () => {
    let params = {
      taskId: taskId,
      name: tagSearchValue.value
    }
    let { data } = await dmpTaskApi.taskTag(params)
    let arr = checkedListItem.value?.map((item: any) => item.id) || []
    if (arr.length > 0) {
      data = data.map((item: any) => {
        if (arr.includes(item.id)) {
          let index = arr.findIndex((i: any) => i === item.id)
          if (checkedListItem.value[index].name !== item.name) {
            checkedListItem.value[index].name = item.name
          }
          item.checked = true
        }
        return item
      })
    }
    checkedList.value = data.filter((item: any) => item.checked).map((item: any) => item.id)
    tagList.value = data
  }

  //标签状态
  const inputRef: Ref<HTMLInputElement | null> = ref(null)

  const showTagIsSearch = (type: boolean): void => {
    if (isEditing.value) {
      message.error('请先完成当前正在编辑的标签')
      return
    }
    tagIsSearch.value = type
    if (type) {
      nextTick(() => inputRef.value?.focus())
    }
  }

  // 新增标签
  const addTag = () => {
    if (isEditing.value) {
      message.error('请先完成当前正在编辑的标签')
      return
    }
    let tag: TaskTag = { name: '', id: '0', addEdit: true }
    tagList.value.unshift(tag)
    editTagIndex.value = 0
    editTagValue.value = ''
    disableSelectAll.value = true
  }
  // 点击开始编辑标签
  const editTag = (item: any, index: any) => {
    if (isEditing.value) {
      message.error('请先完成当前正在编辑的标签')
      return
    }
    editTagIndex.value = index
    editTagValue.value = item.name
    disableSelectAll.value = true
  }
  // 确定编辑标签内容
  const confirmEditTag = async (item: any, index: any) => {
    if (!editTagValue.value) {
      message.error('标签名称不能为空')
      return
    }
    if (!regex.basicName.test(editTagValue.value)) {
      message.error('仅支持中英文、数字、横线、下划线、括号、小数点')
      return
    }
    let paramsAdd = {
      name: editTagValue.value,
      taskId: taskId,
      attributeList: [],
      color: ''
    }
    let paramsEdit = {
      id: item.id,
      name: editTagValue.value,
      attributeList: [],
      color: ''
    }
    let { data } = item.addEdit
      ? await dmpTaskApi.taskTagAdd(paramsAdd, {
          isAddUid: true
        })
      : await dmpTaskApi.taskTagEdit(paramsEdit, {
          isAddUid: true
        })
    if (data) {
      if (item.addEdit === true) {
        tagList.value[index].addEdit = false
      }

      await getTaskTag()
      editTagIndex.value = -1
      editTagValue.value = undefined
    }
    disableSelectAll.value = false
  }
  // 取消编辑标签
  const cancelEditTag = (item: TaskTag) => {
    if (item.addEdit === true) {
      tagList.value.splice(0, 1)
      editTagValue.value = ''
      editTagIndex.value = -1
    } else {
      editTagValue.value = ''
      editTagIndex.value = -1
    }
    disableSelectAll.value = false
  }
  // 删除标签
  const deleteTag = (item: TaskTag) => {
    if (isEditing.value) {
      message.error('请先完成当前正在编辑的标签')
      return
    }
    Modal.confirm({
      title: '确定删除?',
      icon: createVNode(WarnIcon),
      content: '删除后，标签不可恢复',
      okText: '确定',
      cancelText: '取消',
      centered: true,
      onOk: async () => {
        let { data } = await dmpTaskApi.taskTagDel({}, { urlParams: { id: item.id } })
        if (data) {
          let index = checkedList.value.findIndex((items: string | undefined) => items === item.id)
          if (index !== -1) {
            checkedList.value = checkedList.value.filter((items: string | undefined) => items !== item.id)
            checkedListItem.value = checkedListItem.value.filter((items: any) => items.id !== item.id)
          }

          await getTaskTag()
          await getTaskProgress()
          await getListData()
        }
      }
    })
  }
  // 选择标签
  const onCheckAllChange = (e: any) => {
    checkedList.value = e.target.checked ? tagList.value.map(item => item.id) : []

    // 根据选中状态判断是添加还是删除
    tagList.value.forEach(tag => {
      const tagInCheckedListIndex = checkedListItem.value.findIndex(t => t.id === tag.id)
      console.log('tagInCheckedListIndex', tagInCheckedListIndex)

      // 选中
      if (e.target.checked) {
        // 已有此标签，则不做任何操作
        if (tagInCheckedListIndex > -1) {
          return
        }
        checkedListItem.value.push(tag)
        return
      }
      // 取消选中
      if (tagInCheckedListIndex > -1) {
        checkedListItem.value.splice(tagInCheckedListIndex, 1)
      }
    })
  }
  const onCheckChange = (e: any) => {
    checkedList.value = e
    let arr = [...checkedListItem.value, ...tagList.value.filter(item => checkedList.value.includes(item.id))]
    checkedListItem.value = arr.filter(
      (obj: { id: any }, index: any, self: any[]) => index === self.findIndex((t: { id: any }) => t.id === obj.id)
    )

    console.log('-------', checkedListItem.value)
  }
  const onItemChange = (e: any) => {
    // 用作取消
    if (!e.target.checked) {
      checkedListItem.value = checkedListItem.value.filter((item: any) => item.id !== e.target.value)
    }
  }
  const getTaskDetail = async (id: string) => {
    let { data } = await dmpTaskApi.detail(id)
    taskInfo.value = data
  }

  /*  页面跳转 -------------------------------------------------------------------------------------------------------------------------*/
  // 返回上一页
  const backPage = () => {
    router.push(`/dmp/detail/img/${taskId}`)
  }

  /*   -------------------------------------------------------------------------------------------------------------------------*/
  useEventListener(window, 'keydown', handleKeyDown)
  useEventListener(window, 'resize', async () => {
    await nextTick()
    if (!canvas) {
      return
    }
    canvas.setWidth(middleMarkRef.value.offsetWidth - 180)
    canvas.setHeight(markAreaRef.value.offsetHeight)
  })

  onMounted(async () => {
    const itemId = route.query.id as string

    openLoading({ tip: '页面加载中' })
    await getTaskProgress()
    getTaskDetail(taskId)
    await getTaskTag()
    createMockImgList(taskProgressData.value.totalCount)
    const startIndex = await getImgStartIndex(taskId, itemId)
    await getPartImgList(startIndex - 10 >= 0 ? startIndex - 10 : 0) //防止图片列表中间，显示不全
    initCanvas()

    selectImg(startIndex)
  })
</script>

<style lang="less" scoped>
  @import '@/styles/mixins.less';

  :deep(.ant-checkbox-group) {
    width: 100%;

    .ant-checkbox-wrapper {
      width: 100%;
    }

    .ant-checkbox + span {
      width: 90%;
    }
  }

  .flex-between {
    display: flex;
    justify-content: space-between;
    align-items: center;
  }

  .task-mark {
    height: 100vh;
    background: #fff;

    .header {
      padding-right: 24px;
      height: 56px;
      border-bottom: 1px solid #d9d9db;

      .left-info {
        display: flex;
        align-items: center;
        height: 100%;

        .logo {
          padding: 0 25px;
          height: 100%;
          border-right: 1px solid #d9d9db;

          img {
            width: 28px;
            height: 28px;
            object-fit: cover;
          }
        }

        .mark-info {
          display: flex;
          align-items: center;

          .task-name {
            margin-left: 24px;
            max-width: 600px;
            color: rgba(25, 25, 48, 0.45);
            cursor: pointer;
            .elliptical-1();
          }

          .mark-title {
            margin-left: 8px;
            color: rgba(16, 16, 20, 0.95);
            .font16b();
          }

          .task-type {
            display: inline-block;
            padding: 3px 6px;
            margin-left: 16px;
            color: #d61860;
            background: rgba(214, 24, 96, 0.08);
            border: 1px solid rgba(0, 0, 0, 0.05);
            border-radius: 6px;
            line-height: 22px;
          }
        }
      }

      .right-progress {
        width: 133px;
      }
    }

    .contain {
      height: calc(100% - 58px);

      .middle-mark {
        position: relative;
        padding-top: 48px;
        width: calc(100% - 156px - 320px);
        height: 100%;

        .utils-bar {
          position: absolute;
          top: 0;
          display: flex;
          justify-content: space-between;
          align-items: center;
          padding: 4px 8px;
          width: 100%;
          height: 48px;
          color: rgba(16, 16, 20, 0.95);
          background: #fff;
          box-shadow: 0 1px 3px rgba(37, 37, 45, 0.16);

          .change-btn {
            display: flex;
            padding-right: 90px;

            .btn {
              display: flex;
              justify-content: center;
              align-items: center;
              width: 32px;
              height: 40px;
              text-align: center;
              border-radius: 6px;

              &:last-child {
                margin-left: 4px;
              }
            }
          }

          .mark-tools {
            display: flex;
            justify-content: center;
            align-items: center;
            width: 100%;
            // height: 20px;
            font-size: 18px;

            .tools-btn {
              position: relative;
              margin-right: 22px;
              width: 26px;
              height: 26px;
              text-align: center;
              line-height: 26px;
              cursor: pointer;

              &:hover,
              &--active {
                background: rgba(232, 232, 235, 1);
                border-radius: 6px;
              }

              i {
                font-size: 18px;
                color: #000;
                font-weight: 500;
              }

              &:last-child {
                margin-right: 0;
              }
            }

            span {
              margin-right: 14px;
              cursor: pointer;

              &:hover {
                background: rgba(232, 232, 235, 1);
                border-radius: 6px;
              }

              img {
                width: 18px;
                height: 18px;
                object-fit: cover;
              }
            }

            .empty {
              width: 180px;
            }
          }

          .save-btn {
            display: flex;
            justify-content: flex-end;
            width: 172px;
            flex-shrink: 0;

            .btn {
              display: flex;
              justify-content: center;
              align-items: center;
              width: 82px;
              background: rgb(214, 24, 96);

              span {
                &:first-child {
                  margin-right: 8px;
                }
              }

              &:hover {
                background: rgba(214, 24, 96, 0.88);
              }
            }
          }
        }

        .mark-do-result {
          display: flex;
          justify-content: space-between;
          height: 100%;
          background: #fafafa;

          .mark-area {
            display: flex;
            justify-content: center;
            align-items: center;
            overflow: hidden;
            background: #dbdbdb;
            flex: 1;
          }

          .mark-result {
            display: flex;
            padding-top: 12px;
            width: 180px;
            background: #fafafa;
            border-left: 1px solid #d9d9db;
            flex-direction: column;

            .result-top {
              display: flex;
              justify-content: space-between;
              align-items: center;
              padding: 0 16px;
              height: 22px;

              span {
                .font14b();

                color: rgba(25, 25, 48, 0.75);
              }

              .iconfont icon-delete-bin-2-line {
                font-size: 16px;
                color: rgba(25, 25, 48, 0.2);
                cursor: pointer;
              }
            }

            .result-list {
              overflow-y: auto;
              padding: 0 16px;
              padding-top: 20px;
              width: 100%;
              flex-basis: 1;
              flex-grow: 1;

              .result-li {
                padding: 4px 8px;
                margin-bottom: 12px;
                color: rgba(16, 16, 20, 0.95);
                background: rgba(16, 16, 20, 0.08);
                border: 1px solid rgba(0, 0, 0, 0.05);
                border-radius: 6px;
                line-height: 22px;
                .elliptical-1();
              }
            }
          }
        }
      }

      .right-tag {
        width: 320px;
        height: 100%;
        border-left: 1px solid #d9d9db;

        .tag-tab {
          height: 66px;

          .no-search {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 20px 20px 20px 24px;

            .name {
              color: rgba(25, 25, 48, 0.75);
              .font14b();
            }

            .add-btn {
              display: inline-block;
              margin-left: 17px;
              width: 26px;
              height: 26px;
              font-size: 16px;
              text-align: center;
              color: rgba(25, 25, 48, 0.75);
              border-radius: 6px;
              line-height: 26px;
              cursor: pointer;

              &:hover {
                background: rgba(0, 0, 0, 0.05);
              }
            }

            .search-icon {
              display: inline-block;
              width: 26px;
              height: 26px;
              font-size: 16px;
              text-align: center;
              color: rgba(25, 25, 48, 0.75);
              cursor: pointer;
              line-height: 26px;
              border-radius: 6px;

              &:hover {
                background: rgba(0, 0, 0, 0.05);
              }
            }
          }

          .search {
            padding: 12px 24px;

            .close-search-btn {
              cursor: pointer;
              color: rgba(25, 25, 48, 0.75);
            }
          }
        }

        .check-all-box {
          padding-left: 16px;
          border-bottom: 1px solid #e8e8eb;
          height: 44px;

          .check-all-text {
            padding-left: 24px;
            color: rgba(25, 25, 48, 0.45);
            .font14b();
          }
        }

        .tag-list {
          overflow-y: auto;
          padding: 8px 0;
          height: calc(100% - 106px - 8px);

          .tag-li--active {
            background: #f5f5f5;
          }

          .checkbox-li {
            padding-left: 16px;
            margin-bottom: 8px;
            width: 100%;

            .tag-li {
              display: flex;
              justify-content: space-between;
              align-items: center;
              padding: 11px 24px;
              width: 250px;
              height: 44px;

              &:hover {
                background: #f5f5f5;
              }

              &--active {
                background: #f5f5f5;
              }

              .tag-name {
                width: calc(100% - 68px);
                .elliptical-1();
              }

              .hover-btn {
                .btn {
                  display: inline-block;
                  width: 26px;
                  height: 26px;
                  font-size: 14px;
                  text-align: center;
                  color: rgba(16, 16, 20, 0.95);
                  border-radius: 6px;
                  cursor: pointer;
                  line-height: 26px;

                  &:hover {
                    background: rgba(0, 0, 0, 0.05);
                  }

                  &:last-child {
                    margin-left: 16px;
                  }
                }
              }

              .edit-btn {
                display: flex;

                .btn {
                  cursor: pointer;
                  display: inline-block;
                  width: 24px;
                  height: 24px;
                  text-align: center;
                  border: 1px solid #d9d9db;
                  border-radius: 6px;
                  line-height: 24px;

                  &:first-child {
                    margin-right: 8px;
                    color: #fff;
                    background: #d61860;
                    border: 1px solid #d61860;
                  }
                }
              }
            }
          }
        }
      }
    }
  }

  .ant-checkbox-wrapper {
    :deep(.ant-checkbox-input) {
      &:focus-visible + .ant-checkbox-inner {
        outline: none;
      }
    }
  }
</style>
